package cc.zstart.generator.mybatis.service;

import static org.mybatis.generator.internal.util.JavaBeansUtil.getSetterMethodName;
import static org.mybatis.generator.internal.util.StringUtility.stringHasValue;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import cc.zstart.generator.mybatis.plugin.AmanPluginConfig;
import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.dom.java.CompilationUnit;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
import org.mybatis.generator.api.dom.java.Interface;
import org.mybatis.generator.api.dom.java.JavaVisibility;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.Parameter;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.codegen.AbstractJavaGenerator;

/**
 * @desc <p></p>
 * @author DaiJiangGuo
 * @date 2015年5月12日下午1:13:16
 */
public class ServiceImplGenerator extends AbstractJavaGenerator{
    private AmanPluginConfig apc;
    public Interface interfaze;
    public String mapperProperty;

    public ServiceImplGenerator(AmanPluginConfig apc, Interface interfaze) {
        super();
        this.apc = apc;
        this.setInterfaze(interfaze);
        this.setContext(apc.context);
        this.setIntrospectedTable(apc.introspectedTable);
        initialized();
    }

    public void setInterfaze(Interface interfaze) {
        this.interfaze = interfaze;
    }

    public void initialized() {
        mapperProperty = apc.genProperty(apc.mapperType);
    }

    /* (non-Javadoc)
     * @see org.mybatis.generator.codegen.AbstractJavaGenerator#getCompilationUnits()
     */
    @Override
    public List<CompilationUnit> getCompilationUnits() {
        CommentGenerator commentGenerator = context.getCommentGenerator();

        TopLevelClass topLevelClass = new TopLevelClass(apc.serviceImplType);
        topLevelClass.setVisibility(JavaVisibility.PUBLIC);
        commentGenerator.addJavaFileComment(topLevelClass);

        FullyQualifiedJavaType interfaceType = interfaze.getType();
        topLevelClass.addSuperInterface(interfaceType);
        topLevelClass.addImportedType(interfaceType);

        if (stringHasValue(apc.serviceImplExtends)) {
            FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType(apc.serviceImplExtends);
            List<IntrospectedColumn> pkCols = introspectedTable.getPrimaryKeyColumns();
            if(pkCols != null && pkCols.size() == 1){
                FullyQualifiedJavaType pkType = pkCols.get(0).getFullyQualifiedJavaType();
                topLevelClass.addImportedType(apc.modelType);
                topLevelClass.addImportedType(pkType);
                fqjt.addTypeArgument(new FullyQualifiedJavaType(pkType.getShortName()));
                fqjt.addTypeArgument(new FullyQualifiedJavaType(apc.modelType.getShortName()));
            } else {
                topLevelClass.addImportedType(apc.modelType);
                fqjt.addTypeArgument(new FullyQualifiedJavaType("Object"));
                fqjt.addTypeArgument(new FullyQualifiedJavaType(apc.modelType.getShortName()));
            }
            topLevelClass.addImportedType(fqjt);
            topLevelClass.setSuperClass(new FullyQualifiedJavaType(fqjt.getShortName()));
        }

        if (stringHasValue(apc.serviceImplImplements)) {
            FullyQualifiedJavaType fqjt = new FullyQualifiedJavaType(apc.serviceImplExtends);
            topLevelClass.addSuperInterface(fqjt);
            topLevelClass.addImportedType(fqjt);
        }

        topLevelClass.addAnnotation("@Service");
        topLevelClass.addImportedType("org.springframework.stereotype.Service");

        commentGenerator.addClassComment(topLevelClass, introspectedTable);

        //addMapperFiled(topLevelClass);

        List<Method> ms = interfaze.getMethods();
        for (Method m : ms) {
            if (apc.serviceMethod_save.equals(m.getName())) {
                addSaveMethod(topLevelClass);
            } else if (apc.serviceMethod_saveSelective.equals(m.getName())) {
                addSaveSelectiveMethod(topLevelClass);
            } else if (apc.serviceMethod_deleteByPk.equals(m.getName())) {
                addDeleteByPkMethod(topLevelClass);
            } else if (apc.serviceMethod_updateByPk.equals(m.getName())) {
                addUpdateByPkMethod(topLevelClass);
            } else if (apc.serviceMethod_updateByPkSelective.equals(m.getName())) {
                addUpdateByPkSelectiveMethod(topLevelClass);
            } else if (apc.serviceMethod_findByPk.equals(m.getName())) {
                addFindByPkMethod(topLevelClass);
            } else if (apc.serviceMethod_queryByMap.equals(m.getName())) {
                addQueryByMapMethod(topLevelClass);
            } else if (apc.serviceMethod_queryByEntity.equals(m.getName())) {
                addQueryByEntityMethod(topLevelClass);
            } else if (apc.serviceMethod_queryByPager.equals(m.getName())) {
                addQueryByPagerMethod(topLevelClass);
            }
        }



        List<CompilationUnit> answer = new ArrayList<CompilationUnit>();
        answer.add(topLevelClass);

        return answer;
    }

    /**
     * @author DaiJiangGuo
     * @data 2015年5月12日 下午1:40:48
     * @param topLevelClass
     */
    private void addMapperFiled(TopLevelClass topLevelClass) {
        topLevelClass.addImportedType(apc.mapperType);

        Field field = new Field();
        field.setVisibility(JavaVisibility.PRIVATE);
        field.setFinal(false);
        field.setStatic(false);
        field.setName(mapperProperty); //$NON-NLS-1$
        field.setType(apc.mapperType); //$NON-NLS-1$
        context.getCommentGenerator().addFieldComment(field, introspectedTable);

        field.addAnnotation("@Autowired");
        topLevelClass.addImportedType("org.springframework.beans.factory.annotation.Autowired");

        topLevelClass.addField(field);
    }

    /**
     * @author DaiJiangGuo
     * @data 2015年5月12日 上午11:15:11
     * @param interfaze
     * @param introspectedTable
     */
    private void addQueryByPagerMethod(TopLevelClass topLevelClass) {
        if(apc.pagerType != null){
            Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
            Method method = new Method();

            method.setVisibility(JavaVisibility.PUBLIC);
            method.setReturnType(apc.pagerType);
            importedTypes.add(apc.pagerType);
            method.setName(apc.serviceMethod_queryByPager);

            importedTypes.add(apc.pagerType);
            method.addParameter(new Parameter(apc.pagerType, "pager")); //$NON-NLS-1$

            context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

            StringBuilder sb = new StringBuilder();
            sb.append("pager.setResult(" + mapperProperty + ".queryByPager(pager));");
            method.addBodyLine(sb.toString());
            sb.setLength(0);
            sb.append("return pager;");
            method.addBodyLine(sb.toString());

            topLevelClass.addImportedTypes(importedTypes);
            topLevelClass.addMethod(method);
        }
    }

    /**
     * @author DaiJiangGuo
     * @data 2015年5月12日 上午11:15:08
     * @param interfaze
     * @param introspectedTable
     */
    private void addQueryByEntityMethod(TopLevelClass topLevelClass) {
        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
        Method method = new Method();

        method.setVisibility(JavaVisibility.PUBLIC);
        FullyQualifiedJavaType returnType = new FullyQualifiedJavaType("java.util.List<"+introspectedTable.getBaseRecordType()+">");
        method.setReturnType(returnType);
        importedTypes.add(returnType);
        method.setName(apc.serviceMethod_queryByEntity);

        FullyQualifiedJavaType parameterType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());

        importedTypes.add(parameterType);
        method.addParameter(new Parameter(parameterType, "entity")); //$NON-NLS-1$

        context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

        StringBuilder sb = new StringBuilder();
        sb.append("return " + mapperProperty + ".queryByEntity(entity);");
        method.addBodyLine(sb.toString());

        topLevelClass.addImportedTypes(importedTypes);
        topLevelClass.addMethod(method);

    }

    /**
     * @author DaiJiangGuo
     * @data 2015年5月12日 上午11:15:04
     * @param interfaze
     * @param introspectedTable
     */
    private void addQueryByMapMethod(TopLevelClass topLevelClass) {
        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
        Method method = new Method();

        method.setVisibility(JavaVisibility.PUBLIC);
        FullyQualifiedJavaType returnType = new FullyQualifiedJavaType("java.util.List<"+introspectedTable.getBaseRecordType()+">");
        method.setReturnType(returnType);
        importedTypes.add(returnType);
        method.setName(apc.serviceMethod_queryByMap);

        FullyQualifiedJavaType parameterType = new FullyQualifiedJavaType("java.util.Map<String, Object>");

        importedTypes.add(parameterType);
        method.addParameter(new Parameter(parameterType, "paramMap")); //$NON-NLS-1$

        context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

        StringBuilder sb = new StringBuilder();
        sb.append("return " + mapperProperty + ".queryByMap(paramMap);");
        method.addBodyLine(sb.toString());

        topLevelClass.addImportedTypes(importedTypes);
        topLevelClass.addMethod(method);

    }

    /**
     * @author DaiJiangGuo
     * @data 2015年5月12日 上午11:15:00
     * @param interfaze
     * @param introspectedTable
     */
    private void addUpdateByPkSelectiveMethod(TopLevelClass topLevelClass) {
        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
        Method method = new Method();

        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(new FullyQualifiedJavaType("int"));
        method.setName(apc.serviceMethod_updateByPkSelective);

        FullyQualifiedJavaType parameterType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());

        importedTypes.add(parameterType);
        method.addParameter(new Parameter(parameterType, "entity")); //$NON-NLS-1$

        context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

        StringBuilder sb = new StringBuilder();
        sb.append("return " + mapperProperty + ".updateByPrimaryKeySelective(entity);");
        method.addBodyLine(sb.toString());

        topLevelClass.addImportedTypes(importedTypes);
        topLevelClass.addMethod(method);

    }

    /**
     * @author DaiJiangGuo
     * @data 2015年5月12日 上午11:14:57
     * @param interfaze
     * @param introspectedTable
     */
    private void addUpdateByPkMethod(TopLevelClass topLevelClass) {
        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
        Method method = new Method();

        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(new FullyQualifiedJavaType("int"));
        method.setName(apc.serviceMethod_updateByPk);

        FullyQualifiedJavaType parameterType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());

        importedTypes.add(parameterType);
        method.addParameter(new Parameter(parameterType, "entity")); //$NON-NLS-1$

        context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

        StringBuilder sb = new StringBuilder();
        sb.append("return " + mapperProperty + ".updateByPrimaryKey(entity);");
        method.addBodyLine(sb.toString());

        topLevelClass.addImportedTypes(importedTypes);
        topLevelClass.addMethod(method);

    }

    /**
     * @author DaiJiangGuo
     * @data 2015年5月12日 上午11:14:55
     * @param interfaze
     * @param introspectedTable
     */
    private void addDeleteByPkMethod(TopLevelClass topLevelClass) {
        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
        Method method = new Method();

        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(new FullyQualifiedJavaType("int"));
        method.setName(apc.serviceMethod_deleteByPk);

        context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

        List<IntrospectedColumn> pkCols = introspectedTable.getPrimaryKeyColumns();
        if(pkCols.size() > 1){
            FullyQualifiedJavaType pkType = new FullyQualifiedJavaType(introspectedTable.getPrimaryKeyType());
            importedTypes.add(pkType);
            method.addBodyLine(pkType.getShortName() + " pk = new "+pkType.getShortName() + "();");
            for(IntrospectedColumn pk : pkCols){
                FullyQualifiedJavaType paramPk = pk.getFullyQualifiedJavaType();
                importedTypes.add(paramPk);
                String setterName = getSetterMethodName(pk.getJavaProperty());
                importedTypes.add(paramPk);
                method.addParameter(new Parameter(paramPk, pk.getJavaProperty()));
                method.addBodyLine("pk."+setterName+"("+pk.getJavaProperty()+");");
            }
            method.addBodyLine("return " + mapperProperty + ".deleteByPrimaryKey(pk);");
        }else if(pkCols.size() == 1){
            IntrospectedColumn pk = pkCols.get(0);
            FullyQualifiedJavaType paramPk = pk.getFullyQualifiedJavaType();
            importedTypes.add(paramPk);
            method.addParameter(new Parameter(paramPk, pk.getJavaProperty()));
            method.addBodyLine("return " + mapperProperty + ".deleteByPrimaryKey("+pk.getJavaProperty()+");");
        }

        topLevelClass.addImportedTypes(importedTypes);
        topLevelClass.addMethod(method);

    }

    /**
     * @author DaiJiangGuo
     * @data 2015年5月12日 上午11:14:52
     * @param interfaze
     * @param introspectedTable
     */
    private void addSaveSelectiveMethod(TopLevelClass topLevelClass) {
        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
        Method method = new Method();

        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));
        method.setName(apc.serviceMethod_saveSelective);

        FullyQualifiedJavaType parameterType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());

        importedTypes.add(parameterType);
        method.addParameter(new Parameter(parameterType, "entity")); //$NON-NLS-1$

        context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

        StringBuilder sb = new StringBuilder();
        sb.append(mapperProperty + ".insertSelective(entity);");
        method.addBodyLine(sb.toString());
        sb.setLength(0);
        sb.append("return entity;");
        method.addBodyLine(sb.toString());

        topLevelClass.addImportedTypes(importedTypes);
        topLevelClass.addMethod(method);
    }

    /**
     * @author DaiJiangGuo
     * @data 2015年5月12日 上午11:14:47
     * @param interfaze
     * @param introspectedTable
     */
    private void addSaveMethod(TopLevelClass topLevelClass) {
        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
        Method method = new Method();

        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));
        method.setName(apc.serviceMethod_save);

        FullyQualifiedJavaType parameterType = new FullyQualifiedJavaType(introspectedTable.getBaseRecordType());

        importedTypes.add(parameterType);
        method.addParameter(new Parameter(parameterType, "entity")); //$NON-NLS-1$

        context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

        StringBuilder sb = new StringBuilder();
        sb.append(mapperProperty + ".insert(entity);");
        method.addBodyLine(sb.toString());
        sb.setLength(0);
        sb.append("return entity;");
        method.addBodyLine(sb.toString());

        topLevelClass.addImportedTypes(importedTypes);
        topLevelClass.addMethod(method);
    }

    /**
     * @author DaiJiangGuo
     * @data 2015年5月11日 下午11:01:45
     * @param interfaze
     */
    private void addFindByPkMethod(TopLevelClass topLevelClass) {
        Set<FullyQualifiedJavaType> importedTypes = new TreeSet<FullyQualifiedJavaType>();
        Method method = new Method();

        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(new FullyQualifiedJavaType(introspectedTable.getBaseRecordType()));
        method.setName(apc.serviceMethod_findByPk);

        context.getCommentGenerator().addGeneralMethodComment(method, introspectedTable);

        List<IntrospectedColumn> pkCols = introspectedTable.getPrimaryKeyColumns();
        if(pkCols.size() > 1){
            FullyQualifiedJavaType pkType = new FullyQualifiedJavaType(introspectedTable.getPrimaryKeyType());
            importedTypes.add(pkType);

            method.addBodyLine(pkType.getShortName() + " pk = new "+pkType.getShortName() + "();");

            for(IntrospectedColumn pk : pkCols){
                FullyQualifiedJavaType paramPk = pk.getFullyQualifiedJavaType();
                importedTypes.add(paramPk);
                String setterName = getSetterMethodName(pk.getJavaProperty());
                importedTypes.add(paramPk);
                method.addParameter(new Parameter(paramPk, pk.getJavaProperty()));
                method.addBodyLine("pk."+setterName+"("+pk.getJavaProperty()+");");
            }
            method.addBodyLine("return " + mapperProperty + ".selectByPrimaryKey(pk);");
        }else if(pkCols.size() == 1){
            IntrospectedColumn pk = pkCols.get(0);
            FullyQualifiedJavaType paramPk = pk.getFullyQualifiedJavaType();
            importedTypes.add(paramPk);
            method.addParameter(new Parameter(paramPk, pk.getJavaProperty()));
            method.addBodyLine("return " + mapperProperty + ".selectByPrimaryKey("+pk.getJavaProperty()+");");
        }

        topLevelClass.addImportedTypes(importedTypes);
        topLevelClass.addMethod(method);
    }


}
